package com.atguigu.gmallgate.way.filter;

import com.atguigu.gmall.common.utils.IpUtils;
import com.atguigu.gmall.common.utils.JwtUtils;
import com.atguigu.gmallgate.way.config.JwtProperties;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.sound.midi.Soundbank;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * @author Jia Fa
 * @create 2021-12-17 18:12
 */
@Component
@EnableConfigurationProperties(JwtProperties.class)
public class AuthGatewayFilterFactory extends AbstractGatewayFilterFactory<AuthGatewayFilterFactory.PathConfig> {

    @Autowired
    private JwtProperties jwtProperties;

    @Override
    public GatewayFilter apply(PathConfig config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

                // 获取请求对象及相应对象
                ServerHttpRequest request = exchange.getRequest();
                ServerHttpResponse response = exchange.getResponse();

                // 1 判断请求是否在请求拦截名单中,不在直接放行
                List<String> paths = config.paths;  // 获取拦截名单
                String curPath = request.getURI().getPath(); // 获取当前访问路径
                if (!CollectionUtils.isEmpty(paths)){
                    // 如果名单所有路径都不满足,直接放行  startsWith 起始
                    if (paths.stream().noneMatch(path->curPath.startsWith(path))){
                        return chain.filter(exchange);
                    }
                }

                // 2 获取请求中的token信息  同步-cookie 异步-头信息 获取
                String token = request.getHeaders().getFirst("token"); //异步请求获取token
                if (StringUtils.isEmpty(token)){
                    // 异步请求没有token  再从同步请求cookie中获取
                    MultiValueMap<String, HttpCookie> cookies = request.getCookies();
                    if (!CollectionUtils.isEmpty(cookies)){
                        HttpCookie cookie = cookies.getFirst(jwtProperties.getCookieName());
                        token = cookie.getValue();
                    }
                }

                // 3 判断token是否为空,为空拦截重定向到登录页面
                if (StringUtils.isEmpty(token)){
                    // 设置重定向信息
                    response.setStatusCode(HttpStatus.SEE_OTHER);// 303
                    //设置重定向信息并设置登录后返回的路径
                    response.getHeaders().set(HttpHeaders.LOCATION, "http://sso.gmall.com/toLogin.html?returnUrl="+request.getURI());
                    // 拦截
                    return response.setComplete();
                }

                try {
                    // 4 解析token 解析异常重定向到登录页面
                    Map<String, Object> map = JwtUtils.getInfoFromToken(token, jwtProperties.getPublicKey());


                    // 5 判断token中的ip和当前请求的ip是否一致 异常则重定向到登录页面
                    String ip = map.get("ip").toString();
                    String curIp = IpUtils.getIpAddressAtGateway(request);
                    if (!StringUtils.equals(ip, curIp)){
                        // 设置重定向信息
                        response.setStatusCode(HttpStatus.SEE_OTHER);// 303
                        //设置重定向信息并设置登录后返回的路径
                        response.getHeaders().set(HttpHeaders.LOCATION, "http://sso.gmall.com/toLogin.html?returnUrl="+request.getURI());
                        // 拦截
                        return response.setComplete();
                    }
                    // 6 把解析后的登录信息传递给后续微服务 省去再次解析的时间
                    request.mutate().header("userId", map.get("userId").toString()).build();
                    exchange.mutate().request(request).build();

                    // 放行
                    return chain.filter(exchange);

                } catch (Exception e) {
                    e.printStackTrace();
                    // 设置重定向信息
                    response.setStatusCode(HttpStatus.SEE_OTHER);// 303
                    //设置重定向信息并设置登录后返回的路径
                    response.getHeaders().set(HttpHeaders.LOCATION, "http://sso.gmall.com/toLogin.html?returnUrl="+request.getURI());
                    // 拦截
                    return response.setComplete();
                }
            }
        };
    }


    // 指定父类的无参构造
    public AuthGatewayFilterFactory() {
        super(PathConfig.class);
    }

    // 指定返回值类型
    @Override
    public ShortcutType shortcutType() {
        return ShortcutType.GATHER_LIST;
    }

    // 指定集合字段  该字段应与静态类中字段一致
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("paths");
    }

    // 不固定配置
    @Data
    public static class PathConfig{
        private List<String> paths;
    }
}
